/**
 * 20220208:增加touchcancel事件的监听，解决VR浏览器touchcancel事件触发引起的问题
 * 20220308:去除强制横屏转换，并去除上一版本过时方法
 * 20220610:1.since there is no useful default action for multiple clicks,avoid them.
 *          2.delete deprecated methods.
 * 20220613:use [.touches checking] to prevent wrong event-type
 * 20220614:extract from global-libs due to [VR-updating is no longer needed by public library].
 * 20221010: rewrite with typescript, massive modification.
 */

const eventNames=[
    "pointEnter",//0
    "pointMove",//1
    "pointOut",//2
    "pointDown",//3
    "pointUp",//4
    "pointClick",//5
    "pointDoubleClick",//6
    "wheel",//7
    "leftPointDown",//8
    "leftPointUp",//9
    "leftPointMove",//10
    "rightPointDown",//11
    "rightPointUp",//12
    "rightPointMove",//13
    "windowBlur",//14
];
class EventType{
    public static readonly pointEnter:string=eventNames[0];
    public static readonly pointMove:string=eventNames[1];
    public static readonly pointOut:string=eventNames[2];
    public static readonly pointDown:string=eventNames[3];
    public static readonly pointUp:string=eventNames[4];
    public static readonly pointClick:string=eventNames[5];
    public static readonly pointDoubleClick:string=eventNames[6];
    public static readonly wheel:string=eventNames[7];
    public static readonly leftPointDown:string=eventNames[8];
    public static readonly leftPointUp:string=eventNames[9];
    public static readonly leftPointMove:string=eventNames[10];
    public static readonly rightPointDown:string=eventNames[11];
    public static readonly rightPointUp:string=eventNames[12];
    public static readonly rightPointMove:string=eventNames[13];
    public static readonly windowBlur:string=eventNames[14];
};
class EventContainer{

    private handlesArray:Array<Array<Handler>>;

    public get pointEnter():Array<Handler>{
        return this.handlesArray[0];
    }
    public get pointMove():Array<Handler>{
        return this.handlesArray[1];
    }
    public get pointOut():Array<Handler>{
        return this.handlesArray[2];
    }
    public get pointDown():Array<Handler>{
        return this.handlesArray[3];
    }
    public get pointUp():Array<Handler>{
        return this.handlesArray[4];
    }
    public get pointClick():Array<Handler>{
        return this.handlesArray[5];
    }
    public get pointDoubleClick():Array<Handler>{
        return this.handlesArray[6];
    }
    public get wheel():Array<Handler>{
        return this.handlesArray[7];
    }
    public get leftPointDown():Array<Handler>{
        return this.handlesArray[8];
    }
    public get leftPointUp():Array<Handler>{
        return this.handlesArray[9];
    }
    public get leftPointMove():Array<Handler>{
        return this.handlesArray[10];
    }
    public get rightPointDown():Array<Handler>{
        return this.handlesArray[11];
    }
    public get rightPointUp():Array<Handler>{
        return this.handlesArray[12];
    }
    public get rightPointMove():Array<Handler>{
        return this.handlesArray[13];
    }
    public get windowBlur():Array<Handler>{
        return this.handlesArray[14];
    }
    public getContainer(eventType:string):Array<Handler>|null{
        const index= eventNames.indexOf(eventType);
        if(index<0)return null;
        return this.handlesArray[index];
    }
    public clear():void{
        for(let handles of this.handlesArray){
            handles.length=0;
        }
    }

    constructor(){
        this.handlesArray=[];
        for(let i=0;i<eventNames.length;i++){
            this.handlesArray.push([]);
        }
    }
}
class CrossEventObject{
   isMouse?:boolean;
   clientX?:number;
   clientY?:number;
   touches?:Array<{clientX:number,clientY:number}>;
   buttons?:number;
   [key:string]:any;
}
interface Handler{
    (eventObject:CrossEventObject):void;
}

const doEvent = (eventHandles:Array<Handler>,e:CrossEventObject):void=>{
    for(let handle of eventHandles){
        handle(e);
    }
};

const setEventData = (e:CrossEventObject):CrossEventObject=>{
    let isMouse = e.touches?false:true;
    let event = e;
    event.isTouch = !isMouse;
    event.isMouse = isMouse;
    return event;
};

class CrossEvent{

    private _clickInterval=200;
    private _eventContainer:EventContainer;
    private _element:HTMLDivElement|Window;
    private _listenDoubleClick:boolean;
    private _clickTimeListener:any;
    private _isStart:boolean;
    private _touchStartHandle!:Handler;
    private _touchMoveHandle!:Handler;
    private _touchEndHandle!:Handler;
    private _mouseOverHandle!:Handler;
    private _mouseMoveHandle!:Handler;
    private _mouseOutHandle!:Handler;
    private _mouseDownHandle!:Handler;
    private _mouseUpHandle!:Handler;
    private _clickHandle!:Handler;
    private _wheelHandle!:Handler;
    private _windowBlurHandle!:Handler;

    constructor(element:HTMLDivElement|Window){
        this._eventContainer=new EventContainer();
        this._element=element;
        this._listenDoubleClick=false;
        this._clickTimeListener = null;
        this._isStart=false;
        this.initEventHandler();
    }

    public set doubleClickInterval(ms:number){
        this._clickInterval=Math.max(0,ms);
    }

    public addEventListener(eventType:string,handle:Handler):void{
        const handles=this._eventContainer.getContainer(eventType);
        if(!handles){
            console.warn('found no such event-type:',eventType);
            return;
        }
        if(handles.indexOf(handle)!==-1){
            console.error("this handle already exist!");
            return;
        }
        handles.push(handle);
    }

    public removeEventListener(eventType:string,handle:Handler):void{
        const handles=this._eventContainer.getContainer(eventType);
        if(!handles){
            console.warn('found no such event-type:',eventType);
            return;
        }
        const index = handles.indexOf(handle);
        if(index!==-1){
            handles.splice(index,1);
        }
    }

    public clear():void{
        this._eventContainer.clear();
    }

    public start():void{
        if(this._isStart)return;
        this._isStart = true;
        this._element.addEventListener("touchstart",this._touchStartHandle,{ passive: false });
        this._element.addEventListener("touchmove",this._touchMoveHandle,{ passive: false });
        this._element.addEventListener("touchend",this._touchEndHandle,{ passive: false });
        this._element.addEventListener("mouseover",this._mouseOverHandle,{ passive: false });
        this._element.addEventListener("mousemove",this._mouseMoveHandle,{ passive: false });
        this._element.addEventListener("mouseout",this._mouseOutHandle,{ passive: false });
        this._element.addEventListener("mousedown",this._mouseDownHandle,{ passive: false });
        this._element.addEventListener("mouseup",this._mouseUpHandle,{ passive: false });
        this._element.addEventListener("click",this._clickHandle,{ passive: false });
        this._element.addEventListener("wheel",this._wheelHandle,{ passive: false });
        window.addEventListener("blur",this._windowBlurHandle,{passive:false});
        this._element.addEventListener("touchcancel",this._touchEndHandle,{ passive: false });//20220208
    }

    public end():void{
        if(!this._isStart)return;
        this._isStart = false;
        this._element.removeEventListener("touchstart",this._touchStartHandle);
        this._element.removeEventListener("touchmove",this._touchMoveHandle);
        this._element.removeEventListener("touchend",this._touchEndHandle);
        this._element.removeEventListener("mouseover",this._mouseOverHandle);
        this._element.removeEventListener("mousemove",this._mouseMoveHandle);
        this._element.removeEventListener("mouseout",this._mouseOutHandle);
        this._element.removeEventListener("mousedown",this._mouseDownHandle);
        this._element.removeEventListener("mouseup",this._mouseUpHandle);
        this._element.removeEventListener("click",this._clickHandle);
        this._element.removeEventListener("wheel",this._wheelHandle);
        window.removeEventListener("blur",this._windowBlurHandle);
        this._element.removeEventListener('touchcancel',this._touchEndHandle);//20220208
    }

    public dispose():void{
        this.clear();
        this.end();
    }

    private initEventHandler():void{
        this._touchStartHandle = (e:CrossEventObject)=>{
            const event = setEventData(e);
            event.target._isTouch = true;
            doEvent(this._eventContainer.pointEnter,event);
            doEvent(this._eventContainer.pointDown,event);
            let touchLength = event.touches!.length;
            if(touchLength===1){
                doEvent(this._eventContainer.leftPointDown,event);
            }else if(touchLength===2){
                doEvent(this._eventContainer.leftPointUp,event);
                doEvent(this._eventContainer.rightPointDown,event);
            }else if(touchLength===3){
                doEvent(this._eventContainer.rightPointUp,event);
            }
        };
        this._touchMoveHandle = (e:CrossEventObject)=>{
            const event = setEventData(e);
            event.target._isTouch = false;
            doEvent(this._eventContainer.pointMove,event);
            if(event.touches!.length===1){
                doEvent(this._eventContainer.leftPointMove,event);
            }
            else if(event.touches!.length===2){
                doEvent(this._eventContainer.rightPointMove,event);
            }
        };
        this._touchEndHandle = (e:CrossEventObject)=>{
            const event = setEventData(e);
            doEvent(this._eventContainer.pointUp,event);
            doEvent(this._eventContainer.pointOut,event);
            let touchLength = event.touches!.length;
            if(touchLength===2){
                doEvent(this._eventContainer.rightPointDown,event);
            }else if(touchLength===1){
                doEvent(this._eventContainer.rightPointUp,event);
                doEvent(this._eventContainer.leftPointDown,event);
            }else if(touchLength===0){
                doEvent(this._eventContainer.leftPointUp,event);
            }
        };
        this._mouseOverHandle = (e:CrossEventObject)=>{
            const event = setEventData(e);
            doEvent(this._eventContainer.pointEnter,event);
        };
        this._mouseMoveHandle = (e:CrossEventObject)=>{
            if(e.target._isTouch){
                return;
            }
            const event = setEventData(e);
            doEvent(this._eventContainer.pointMove, event);
            if (event.buttons===1)
                doEvent(this._eventContainer.leftPointMove, event);
            else if (event.buttons === 2)
                doEvent(this._eventContainer.rightPointMove, event);
        };
        this._mouseOutHandle = (e:CrossEventObject)=>{
            const event = setEventData(e);
            doEvent(this._eventContainer.pointOut,event);
        };
        this._mouseDownHandle = (e:CrossEventObject)=>{
            if(e.target._isTouch){
                return;
            }
    
            /**
             * 屏蔽双击，三击...默认行为........20220610
             */
            if(e.detail){
                if(e.detail>1)e.preventDefault();
            }
    
            const event = setEventData(e);
            doEvent(this._eventContainer.pointDown, event);
            if (event.button === 0)
                doEvent(this._eventContainer.leftPointDown, event);
            else if (event.button === 2)
                doEvent(this._eventContainer.rightPointDown, event);
        };
        this._mouseUpHandle = (e:CrossEventObject)=>{
            if(e.target._isTouch){
                return;
            }
            const event = setEventData(e);
            doEvent(this._eventContainer.pointUp, event);
            if (event.button === 0)
                doEvent(this._eventContainer.leftPointUp, event);
            else if (event.button === 2)
                doEvent(this._eventContainer.rightPointUp, event);
        };
        this._clickHandle = (e:CrossEventObject)=>{
            const event = setEventData(e);
            event.target._isTouch = false;
            doEvent(this._eventContainer.pointClick, event);
            if(this._listenDoubleClick){
                this._listenDoubleClick = false;
                clearTimeout(this._clickTimeListener);
                doEvent(this._eventContainer.pointDoubleClick, event);
            }else{
                this._listenDoubleClick = true;
                this._clickTimeListener = setTimeout(()=>{
                    this._listenDoubleClick = false;
                },this._clickInterval);
            }
        };
        this._wheelHandle = (e:CrossEventObject)=>{
            e.isMouse = true;
            doEvent(this._eventContainer.wheel,e);
        };
        this._windowBlurHandle = (e:CrossEventObject)=>{
            if(e.target === window){
                doEvent(this._eventContainer.windowBlur,e);
            }
        };
    }

}

export {
    EventType,
    CrossEvent,
    CrossEventObject
}